/**
  ******************************************************************************
  * @file    esp8266.c
  * @author  ashuai0110
  * @version V2.3
  * @date    2024-07-01
  * @brief   ESP8266模组AT指令集
  *
  ******************************************************************************
  * @attention
  *
  * 版权声明:内容为编者(ashuai0110)原创,使用请注明出处,当然,你也可以不这样做^_^
  * 出处链接:https://gitee.com/ashuai0110/mcu_reuse_development_module.git
  *
  * Change Logs:
  * Date          Author        Notes
  * 2024-03-09    ashuai0110    完成基本内容
  * 2024-03-26    ashuai0110    增加联网,SNTP,TCP GET相关接口
  *
  ******************************************************************************
  */

/* 包含头文件-----------------------------------------------------------------*/
#include "esp8266.h"

/**
 * @addtogroup devices
 * @{
 */

/**
 * @defgroup esp8266 esp8266
 * @{
 */

/* 私有宏定义-----------------------------------------------------------------*/
/* WIFI名称和密码 */
#define WIFI_SSID               "MNIAS"
#define WIFI_PWD                "1234567890"

/* 实时时间API NowAPI网站提供 */
#define TIME_REMOTE_ADDR        "103.205.5.228" /* 远程地址 */
#define TIME_REMOTE_PORT        (80u)           /* 远程端口 */
#define TIME_API_APPKEY         "62987"
#define TIME_API_SIGN           "7310da9ba4f82cd822b861cea2a387a7"
#define REAL_TIME_API           "https://sapi.k780.com/?app=life.time&appkey="TIME_API_APPKEY"&sign="TIME_API_SIGN"&format=json"

/* 实时天气API 心知天气网站提供 */
#define WEATHER_REMOTE_ADDR     "116.62.81.138" /* 远程地址 */
#define WEATHER_REMOTE_PORT     (80u)           /* 远程端口 */
#define WEATHER_KEY             "Syma8NgtbsU1_xJ34"
#define WEATHER_LOCATION        "nanjing"
#define REAL_WEATHER_API        "https://api.seniverse.com/v3/weather/now.json?key="WEATHER_KEY"&location="WEATHER_LOCATION"&language=zh-Hans&unit=c"

/* 私有类型定义---------------------------------------------------------------*/

/* 私有变量-------------------------------------------------------------------*/
/**
 * @defgroup esp8266_local_variables esp8266 local variables
 * @{
 */

static esp8266_t esp8266; /*!< esp8266管理实例 */

/**
 * @}
 */

/* 全局变量-------------------------------------------------------------------*/

/* 私有函数原型---------------------------------------------------------------*/
/**
 * @defgroup esp8266_local_functions esp8266 local functions
 * @{
 */

/**
  * @brief  删除指定字符并重组字符串
  *
  * @param  str    : 重组的字符串
  *
  * @param  target : 删除的字符
  *
  * @retval None
  */
static void esp8266_delete_char(char *str, char target)
{
    uint16_t i, j;
    
    for(i = 0, j = 0; i < strlen(str); i++)
    {
        if(str[i] != target)
        {
            str[j++] = str[i];
        }
    }
    str[j] = '\0';
}

/**
  * @brief  处理心知天气json数据
  *
  * @param  src      : json源数据
  *
  * @param  _weather : 存放天气信息的结构体
  *
  * @retval None
  */
static void weather_json_handle(char *src, weather_t *_weather)
{
    cJSON *res, *now, *res2;
    cJSON *cjson;
    
    esp8266_delete_char(src, '[');
    esp8266_delete_char(src, ']');
    cjson = cJSON_Parse(src);
    if(cjson)
    {
        res = cJSON_GetObjectItem(cjson, "results");
        now = cJSON_GetObjectItem(res, "now");
        res2 = cJSON_GetObjectItem(now, "code");
        _weather->weatherCode = atoi(res2->valuestring);
        res2 = cJSON_GetObjectItem(now, "temperature");
        if(*res2->valuestring < '0')
        {
            _weather->temperature = 0;
        }
        else
        {
            _weather->temperature = atoi(res2->valuestring);
        }
        res2 = cJSON_GetObjectItem(now, "pressure");
        _weather->pressure = atoi(res2->valuestring);
        res2 = cJSON_GetObjectItem(now, "humidity");
        _weather->humidity = atoi(res2->valuestring);
        res2 = cJSON_GetObjectItem(now, "wind_scale");
        _weather->windScale = atoi(res2->valuestring);
        DEBUG_LOG("tianqi:%d, temperature:%d, pressure:%d, humidity:%d, windScale:%d", _weather->weatherCode, _weather->temperature, _weather->pressure, _weather->humidity, _weather->windScale);
    }
    else
    {
        ERROR_LOG("weather json error!");
    }
    cJSON_Delete(cjson);
}

/**
 * @}
 */

/**
 * @defgroup esp8266_global_functions esp8266 global functions
 * @{
 */

/**
  * @brief  esp8266初始化
  *
  * @param  readFn  : 读数据接口
  *
  * @param  writeFn : 写数据接口
  *
  * @retval None
  */
void esp8266_init(cmd_at_rw_fn_t readFn, cmd_at_rw_fn_t writeFn)
{
    cmd_at_init(&esp8266.esp8266CmdAt, ESP8266_HEAD, ESP8266_TAIL, readFn, writeFn, 3000, 0);
}

/**
  * @brief  esp8266置位外部接收标志
  *
  * @note   在接收完成的地方调用
  *
  * @param  None
  *
  * @retval None
  */
void esp8266_set_rec_flag(void)
{
    cmd_at_set_rec_flag(&esp8266.esp8266CmdAt);
}

/**
  * @brief  esp8266获取管理实例
  *
  * @param  None
  *
  * @retval 返回esp8266管理实例
  */
esp8266_t *esp8266_get_this(void)
{
    return &esp8266;
}

/**
  * @brief  连接WIFI
  *
  * @param  None
  *
  * @retval 返回结果
  *    @arg RET_OK  : 连接成功
  *    @arg RET_ERR : 连接失败
  *    @arg RET_ING : 运行中
  */
uint8_t esp8266_connect_wifi(void)
{
    uint8_t ret = RET_ING;
    
    switch(esp8266.state)
    {
        case 0:
            /* 关闭回显  Tx:ATE0\r\n  Rx:OK */
            ret = cmd_at_process_poll(&esp8266.esp8266CmdAt, CMD_AT_RESERVE, CMD_AT_WAY_REC_FLAG, ESP8266_W_ATE_RET, ESP8266_W_ATE, 0);
            if(RET_ERR == ret || RET_TIMEOUT == ret)
            {
                ret = RET_ERR;
                esp8266.state = 0;
                ERROR_LOG("close re-display error!");
            }
            else if(RET_OK == ret)
            {
                ret = RET_ING;
                esp8266.state += 1;
            }
            break;
        case 1: /* 设置WIFI模式为STA  Tx:AT+CWMODE=1\r\n  Rx:OK */
            ret = cmd_at_process_poll(&esp8266.esp8266CmdAt, CMD_AT_RESERVE, CMD_AT_WAY_REC_FLAG, ESP8266_W_CWMODE_RET, ESP8266_W_CWMODE, 1);
            if(RET_ERR == ret || RET_TIMEOUT == ret)
            {
                ret = RET_ERR;
                esp8266.state = 0;
                ERROR_LOG("set wifi mode sta error!");
            }
            else if(RET_OK == ret)
            {
                ret = RET_ING;
                esp8266.state += 1;
            }
            break;
        case 2: /* 设置WIFI参数并连接WIFI  Tx:AT+CWJAP="ssid","pwd"\r\n  Rx:GOT IP */
            ret = cmd_at_process_poll(&esp8266.esp8266CmdAt, CMD_AT_RESERVE, CMD_AT_WAY_REC_FLAG, ESP8266_W_CWJAP_RET, ESP8266_W_CWJAP, WIFI_SSID, WIFI_PWD);
            if(RET_ERR == ret || RET_TIMEOUT == ret)
            {
                ret = RET_OK; /* 因为连接时间较长,回复内容多段且间隔大,故不做错误处理 */
                esp8266.state = 0;
            }
            else if(RET_OK == ret)
            {
                esp8266.state = 0;
            }
            break;
    }
    
    return ret;
}

/**
  * @brief  SNTP获取时间
  *
  * @param  None
  *
  * @retval 返回结果
  *    @arg RET_OK  : 连接成功
  *    @arg RET_ERR : 连接失败
  *    @arg RET_ING : 运行中
  */
uint8_t esp8266_sntp(void)
{
    uint8_t ret = RET_ING;
    
    switch(esp8266.state)
    {
        case 0:
            /* 设置SNTP  Tx:AT+CIPSNTPCFG=1,8,"xxx"\r\n  Rx:OK */
            ret = cmd_at_process_poll(&esp8266.esp8266CmdAt, CMD_AT_RESERVE, CMD_AT_WAY_REC_FLAG, ESP8266_W_CIPSNTPCFG_RET, ESP8266_W_CIPSNTPCFG);
            if(RET_ERR == ret || RET_TIMEOUT == ret)
            {
                ret = RET_ERR;
                esp8266.state = 0;
                ERROR_LOG("set sntp error!");
            }
            else if(RET_OK == ret)
            {
                ret = RET_ING;
                esp8266.state += 1;
            }
            break;
        case 1: /* 读取SNTP时间  Tx:AT+CIPSNTPTIME?\r\n  Rx:+CIPSNTPTIME: */
            ret = cmd_at_process_poll(&esp8266.esp8266CmdAt, CMD_AT_RESERVE, CMD_AT_WAY_REC_FLAG, ESP8266_R_CIPSNTPTIME_RET, ESP8266_R_CIPSNTPTIME);
            if(RET_ERR == ret || RET_TIMEOUT == ret)
            {
                ret = RET_ERR;
                esp8266.state = 0;
                ERROR_LOG("read sntp error!");
            }
            else if(RET_OK == ret)
            {
                module_time_sntp_conv(&esp8266.sntpTime, cmd_at_rsp_data(&esp8266.esp8266CmdAt));
                module_time_stamp_init(module_time_stamp_conv(esp8266.sntpTime));
                esp8266.state = 0;
            }
            break;
    }
    
    return ret;
}

/**
  * @brief  建立TCP使用GET方法获取API内容(心知天气)
  *
  * @param  None
  *
  * @retval 返回结果
  *    @arg RET_OK  : 连接成功
  *    @arg RET_ERR : 连接失败
  *    @arg RET_ING : 运行中
  */
uint8_t esp8266_tcp_get_weather(void)
{
    uint8_t ret = RET_ING;
    
    switch(esp8266.state)
    {
        case 0: /* 设置单连接  Tx:AT+CIPMUX=0\r\n  Rx:OK */
            ret = cmd_at_process_poll(&esp8266.esp8266CmdAt, CMD_AT_RESERVE, CMD_AT_WAY_REC_FLAG, ESP8266_W_CIPMUX_RET, ESP8266_W_CIPMUX, 0);
            if(RET_ERR == ret || RET_TIMEOUT == ret)
            {
                ret = RET_ERR;
                esp8266.state = 0;
                ERROR_LOG("set single connect error!");
            }
            else if(RET_OK == ret)
            {
                ret = RET_ING;
                esp8266.state += 1;
            }
            break;
        case 1: /* 建立TCP连接  Tx:AT+CIPSTART="TCP","addr",port\r\n  Rx:CONNECT */
            ret = cmd_at_process_poll(&esp8266.esp8266CmdAt, CMD_AT_RESERVE, CMD_AT_WAY_REC_FLAG, ESP8266_W_CIPSTART_RET, ESP8266_W_CIPSTART, "TCP", WEATHER_REMOTE_ADDR, WEATHER_REMOTE_PORT);
            if(RET_ERR == ret || RET_TIMEOUT == ret)
            {
                ret = RET_ERR;
                esp8266.state = 0;
                ERROR_LOG("set tcp connect error!");
            }
            else if(RET_OK == ret)
            {
                ret = RET_ING;
                esp8266.state += 1;
            }
            break;
        case 2: /* 设置WIFI透传传输模式  Tx:AT+CIPMODE=1\r\n  Rx:OK */
            ret = cmd_at_process_poll(&esp8266.esp8266CmdAt, CMD_AT_RESERVE, CMD_AT_WAY_REC_FLAG, ESP8266_W_CIPMODE_RET, ESP8266_W_CIPMODE, 1);
            if(RET_ERR == ret || RET_TIMEOUT == ret)
            {
                ret = RET_ERR;
                esp8266.state = 0;
                ERROR_LOG("set wifi transmit mode error!");
            }
            else if(RET_OK == ret)
            {
                ret = RET_ING;
                esp8266.state += 1;
            }
            break;
        case 3: /* 进入发送数据模式  Tx:AT+CIPSEND\r\n  Rx:> */
            ret = cmd_at_process_poll(&esp8266.esp8266CmdAt, CMD_AT_RESERVE, CMD_AT_WAY_REC_FLAG, ESP8266_W_CIPSEND_RET, ESP8266_W_CIPSEND);
            if(RET_ERR == ret || RET_TIMEOUT == ret)
            {
                ret = RET_ERR;
                esp8266.state = 0;
                ERROR_LOG("entry transmit mode error!");
            }
            else if(RET_OK == ret)
            {
                ret = RET_ING;
                esp8266.state += 1;
            }
            break;
        case 4: /* 发送数据  Tx:GET https://xxx.com\r\n  Rx:results */
            ret = cmd_at_process_poll(&esp8266.esp8266CmdAt, CMD_AT_RESERVE, CMD_AT_WAY_REC_FLAG, "results", ESP8266_W_GET, REAL_WEATHER_API);
            if(RET_ERR == ret || RET_TIMEOUT == ret)
            {
                ret = RET_ING;
                esp8266.state = 6;
                ERROR_LOG("transmit data error!");
            }
            else if(RET_OK == ret)
            {
                ret = RET_ING;
                esp8266.state += 1;
            }
            break;
        case 5:
//            DEBUG_LOG("ret:%s", cmd_at_rsp_data(&esp8266.esp8266CmdAt));
            weather_json_handle(cmd_at_rsp_data(&esp8266.esp8266CmdAt), &esp8266.weather);
            ret = RET_ING;
            esp8266.state += 1;
            break;
        case 6: /* 退出透传  Tx:+++  Rx: */
            ret = cmd_at_process_poll(&esp8266.esp8266CmdAt, CMD_AT_RESERVE, CMD_AT_WAY_REC_FLAG, NULL, ESP8266_W_EXIT);
            if(RET_ERR == ret)
            {
                ret = RET_ERR;
                esp8266.state = 0;
                ERROR_LOG("exit transmit error!");
            }
            else if(RET_OK == ret  || RET_TIMEOUT == ret)
            {
                ret = RET_ING;
                esp8266.state += 1;
            }
            break;
        case 7: /* 关闭TCP连接  Tx:AT+CIPCLOSE\r\n  Rx:CLOSED */
            ret = cmd_at_process_poll(&esp8266.esp8266CmdAt, CMD_AT_RESERVE, CMD_AT_WAY_REC_FLAG, ESP8266_W_CIPCLOSE_RET, ESP8266_W_CIPCLOSE);
            if(RET_ERR == ret || RET_TIMEOUT == ret)
            {
                ret = RET_ERR;
                esp8266.state = 0;
                ERROR_LOG("tcp disconnect error!");
            }
            else if(RET_OK == ret)
            {
                ret = RET_ING;
                esp8266.state += 1;
            }
            break;
        case 8:
            ret = RET_OK;
            break;
    }
    
    return ret;
}

/**
 * @}
 */

/**
 * @}
 */

/**
 * @}
 */
